#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include "thread.h"
#include "logger.h"

namespace mars {

static pid_t gettid() {
    return syscall(SYS_gettid);
}

namespace CurrentThread {
    __thread const char* t_thread_name = "main";
    __thread pid_t t_thread_tid = 0;

    const char* name() {
        return t_thread_name;
    }

    pid_t tid() {
        if (t_thread_tid == 0) {
            t_thread_tid = gettid();
        }    

        return t_thread_tid;
    }

    bool is_main_thread() {
        return tid() == getpid();
    }
};

struct ThreadData {
    Thread::ThreadFunc _func;
    std::string _name;

    ThreadData(const Thread::ThreadFunc& func, const std::string& name):
         _func(func),
         _name(name)    {
    }

    void run_in_thread() {
        CurrentThread::t_thread_name = _name.empty() ? "mars_thread": _name.c_str();
        // prctl(PR_SET_NAME, CurrentThread::t_thread_name);
        _func();
        CurrentThread::t_thread_name = "finished";
    }
};

static void* start_thread(void *arg) {
    ThreadData *data = static_cast<ThreadData*>(arg);
    data->run_in_thread();
    delete data;
}

// class Thread
Thread::Thread(const ThreadFunc& func, const std::string& name):
     _func(func),
    _name(name),
    _started(false),
    _joined(false),
    _tid(0) {
        if (name.empty()) {
            _name = "mars_thread";
        }
}

Thread::~Thread() {
    DEBUG_PRINT("thread %s destroy\n", name().c_str());
    if (_started && !_joined) {
        DEBUG_PRINT("thread %s deatch\n", name().c_str());
        pthread_detach(_pthread_id);
    }
}

void Thread::start() {
    assert(!_started);
    _started = true;
    DEBUG_PRINT("thread %s start\n", _name.c_str());

    ThreadData* data = new ThreadData(_func, _name);
    int ret = pthread_create(&_pthread_id, NULL, start_thread, data);

    char err_info[128];
    if (ret != 0) {
        errno = ret;
        strerror_r(errno, err_info, 128);
        ERROR_PRINT("%s\n", err_info);
        abort();
    }
}

void Thread::join() {
    assert(_started);
    assert(!_joined);
    _joined = true;
    pthread_join(_pthread_id, NULL);
}

std::string Thread::name() {
    return _name;
}

pid_t Thread::tid() {
    return _tid;
}

} // namespace mars

